package org.sqlproc.engine.impl.type;

import java.lang.reflect.Method;
import java.util.Collection;

import org.hibernate.Query;
import org.hibernate.type.PrimitiveType;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sqlproc.engine.SqlRuntimeException;
import org.sqlproc.engine.impl.BeanUtils;

/**
 * The SQL META Types for HIBERNATE.
 * 
 * @author <a href="mailto:Vladimir.Hudec@gmail.com">Vladimir Hudec</a>
 */
public class SqlHibernateType {

    /**
     * The internal type, which means special processing of the input/output value.
     */
    protected static final Logger logger = LoggerFactory.getLogger(SqlHibernateType.class);

    /**
     * Initializes the attribute of the result class with output values from SQL query execution.
     * 
     * @param resultInstance
     *            the instance of the result class
     * @param attributeName
     *            the name of the attribute in the result class
     * @param resultValue
     *            Query execution output value
     * @param hibernateType
     *            the Hibernate type
     * @param ingoreError
     *            ignore inproper output value handling
     * @throws org.sqlproc.engine.SqlRuntimeException
     *             in the case of any problem with output values handling
     */
    public static void setResult(Object resultInstance, String attributeName, Object resultValue, Type hibernateType,
            boolean ingoreError) throws SqlRuntimeException {
        if (logger.isTraceEnabled()) {
            logger.trace(">>> setResult HIBERNATE: resultInstance=" + resultInstance + ", attributeName="
                    + attributeName + ", resultValue=" + resultValue + ", hibernateType=" + hibernateType);
        }
        Method m = BeanUtils.getSetter(resultInstance, attributeName, hibernateType.getReturnedClass());
        if (m == null && hibernateType instanceof PrimitiveType)
            m = BeanUtils.getSetter(resultInstance, attributeName, ((PrimitiveType) hibernateType).getPrimitiveClass());
        if (m == null && hibernateType.getReturnedClass() == java.util.Date.class)
            m = BeanUtils.getSetter(resultInstance, attributeName, java.sql.Timestamp.class);
        if (m != null) {
            BeanUtils.simpleInvokeMethod(m, resultInstance, resultValue);
        } else if (ingoreError) {
            logger.error("There's no getter for " + attributeName + " in " + resultInstance
                    + ", META type is HIBERNATE");
        } else {
            throw new SqlRuntimeException("There's no setter for " + attributeName + " in " + resultInstance
                    + ", META type is HIBERNATE");
        }
    }

    /**
     * Bind an input value to a named query parameter.
     * 
     * @param query
     *            the object-oriented representation of a Hibernate query
     * @param paramName
     *            the name of the parameter
     * @param inputValue
     *            the possibly-null parameter value, a dynamic input value
     * @param hibernateType
     *            the Hibernate type
     * @param ingoreError
     *            ignore inproper input value handling
     * @throws org.sqlproc.engine.SqlRuntimeException
     *             in the case of any problem with input values handling
     */
    public static void setParameter(Query query, String paramName, Object inputValue, Class<?> inputType,
            Type hibernateType, boolean ingoreError) throws SqlRuntimeException {
        if (logger.isTraceEnabled()) {
            logger.trace(">>> setParameter HIBERNATE: paramName=" + paramName + ", inputValue=" + inputValue
                    + ", inputType=" + inputType + ", hibernateType=" + hibernateType);
        }
        if (inputValue instanceof Collection) {
            query.setParameterList(paramName, ((Collection) inputValue).toArray(), hibernateType);
        } else {
            query.setParameter(paramName, inputValue, hibernateType);
        }
    }
}
